package com.boyuan.system.websocket;

import com.boyuan.system.utils.PreviewUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * websocket服务
 *
 * @author ply
 * @date 2023-11-20 10:47
 */
@Slf4j
@Component
@ServerEndpoint(value = "/websocketServer/{userId}")
public class WebsocketServer {
    //记录当前在线网页数量
    private static AtomicInteger onlineCount = new AtomicInteger(0);

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(final Session session, @PathParam("userId") String userId) {
        onlineCount.addAndGet(1);
        log.info("当前已经登录设备：" + BlockQueue.bqMap.keySet());
        log.info("有新的连接加入：sessionId：" + session.getId() + "，摄像头登录id：" + userId + "，当前在线socket(视频路数)：" + onlineCount);
        if (BlockQueue.bqMap.containsKey(userId)) {
            if (BlockQueue.findPlayIdByUserId(userId) == null) {
                log.info("根据登录信息，没有找到用户预览句柄" + userId);
            } else {
                BlockingQueue blockingQueue = BlockQueue.bqMap.get(userId);
                BlockQueue.sessionToUserIdMap.put(session.getId(), userId);
                BlockQueue.sessionMap.put(session, userId);
                //因为websocket就是多线程，所以直接进行处理逻辑
                while (session.isOpen() && blockingQueue != null) {
                    try {
                        byte[] bytes = (byte[]) blockingQueue.take();
                        if (bytes.length < 1) {
                            log.info("取流失败，暂无内容");
                            continue;
                        }
                        ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
                        session.getBasicRemote().sendBinary(byteBuffer);
                    } catch (InterruptedException | IOException e) {
                        log.info("socket数据发送失败，错误信息为：" + e.getMessage());
                        throw new RuntimeException(e);
                    }
                }
            }
        } else {
            log.info("当前没有找到用户登录，无法播放：" + userId);
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(final Session session) {
        //在线数减1
        onlineCount.decrementAndGet();
        log.info("socket sessionId:" + session.getId() + " 断开连接，查找并执行退出预览和登录");
        //执行退出操作
        if (BlockQueue.sessionToUserIdMap.containsKey(session.getId())) {
            String userId = BlockQueue.sessionToUserIdMap.get(session.getId());
            if (userId != null) {
                log.info("找到正在登录userId：" + userId + "预览的相关信息，执行停止预览并退出登录操作");
                //执行退出预览操作
                PreviewUtil.logoutPlayView(userId);
            }
            BlockQueue.sessionMap.remove(session);
            BlockQueue.bqMap.remove(userId);
        } else {
            BlockQueue.sessionMap.remove(session);
            log.info("没有找到该socket相关登录预览信息");
        }
    }

    /**
     * 收到客户端消息后调用方法
     */
    @OnMessage
    public void onMessage(final String message, final Session session) {
        log.info("服务端收到客户端：" + session.getId() + "，消息内容：" + message);
    }

    /**
     * 连接异常错误调用方法
     */
    @OnError
    public void onError(final Session session, final Throwable throwable) {
        log.info("socket：" + session.getId() + "发生错误，查找并执行退出预览和登录，错误信息：" + throwable.getMessage());
        //执行退出操作
        if (BlockQueue.sessionToUserIdMap.containsKey(session.getId())) {
            String userId = BlockQueue.sessionToUserIdMap.get(session.getId());
            if (userId != null) {
                log.info("找到正在登录userId：" + userId + "预览的相关信息，执行停止预览并退出登录操作");
                //执行退出预览操作
                BlockQueue.sessionMap.remove(session);
                PreviewUtil.logoutPlayView(userId);
            }
            BlockQueue.bqMap.remove(userId);
        } else {
            BlockQueue.sessionMap.remove(session);
            log.info("没有找到该socket相关登录预览信息");
        }
    }

    /**
     * 服务端发送信息给客户端
     */
    private void sendMessage(final String message, final Session session) {
        try {
            log.info("服务端给客户端：" + session.getId() + "发送消息：" + message);
            session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            log.info("服务端发送信息给客户端失败：" + e.getMessage());
            throw new RuntimeException(e);
        }
    }

}
